namespace Xmef.Library
{
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Globalization;
    using System.Reflection;
    using System.Security;
    using System.Text;
    using Xmef.Interfaces;

    public class LogHelper : PluginIdentity, IPluginLog, IPluginIdentity
    {
        private readonly string _name;
        private static readonly IPluginLog Default = new LogHelper("Xmef.Library");
        private static int errorMessageIndex;
        private static int messageIndex;
        private static int outputMessageIndex;
        private static TimeSpan time1 = new TimeSpan(DateTime.Now.Ticks);

        private LogHelper(string name)
        {
            this._name = name;
        }

        private static string FormatException(Exception ex, string assembly, string source)
        {
            StringBuilder text = new StringBuilder();
            text.AppendLine(string.Format(CultureInfo.InvariantCulture, "{0}#{0}{1}{0}{2}{0}#{0}", new object[] { Environment.NewLine, assembly, source }));
            while (ex != null)
            {
                text.AppendLine(string.Format(CultureInfo.InvariantCulture, "{0}#{0}{1}{0}{2}{0}{3}{0}{4}{0}#{0}", new object[] { Environment.NewLine, ex.Source, ex.GetType().Name, ex.StackTrace, ex.Message }));
                ex = ex.InnerException;
            }
            return text.ToString();
        }

        public static void Write(string message)
        {
            WriteAsync(message);
        }

        public static void Write(Exception ex, IPluginIdentity obj)
        {
            Type type = obj.GetType();
            string _source = type.Assembly.ManifestModule.Name;
            string _message = FormatException(ex, _source, type.ToString());
            WriteAsync(string.Format(CultureInfo.InvariantCulture, "{1}{0}{2}", new object[] { Environment.NewLine, ex.GetType(), _message }));
        }

        public void Write(string source, string message)
        {
            this.WriteLogMessage(source, message);
        }

        public static void Write(Exception ex, IPluginIdentity obj, bool defaultLog)
        {
            if (defaultLog)
            {
                Write(ex, obj, Default);
            }
            else
            {
                Write(ex, obj);
            }
        }

        public static void Write(Exception ex, IPluginIdentity obj, IPluginLog log)
        {
            Type type = obj.GetType();
            Assembly assembly = type.Assembly;
            string _message = FormatException(ex, assembly.ManifestModule.Name, type.ToString());
            log.Write(log.Name, _message);
            WriteAsync(string.Format(CultureInfo.InvariantCulture, "{1}{0}{2}", new object[] { Environment.NewLine, ex.GetType(), _message }));
        }

        public static void WriteAsync(string message)
        {
            TimeSpan time2 = new TimeSpan(DateTime.Now.Ticks + 1L);
            ThreadHelper.ActionDelegateWorkItem<string>.QueryAsync(message, delegate (string item) {
                Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "[{0, 6}]:[{1}]:[{2}]", new object[] { ++messageIndex, time2 - time1, item }));
            });
        }

        public static void WriteErrorSync(Exception ex)
        {
            WriteErrorSync(string.Format(CultureInfo.InvariantCulture, "{0}#{0}{1}{0}#{0}{2}{0}#{0}", new object[] { Environment.NewLine, ex.Message, ex.StackTrace }));
        }

        public static void WriteErrorSync(string message)
        {
            TimeSpan time2 = new TimeSpan(DateTime.Now.Ticks + 1L);
            ThreadHelper.ActionDelegateWorkItem<string>.QuerySync(message, delegate (string item) {
                Console.Error.WriteLine(string.Format(CultureInfo.InvariantCulture, "[{0, 6}]:[{1}]:[{2}]", new object[] { ++errorMessageIndex, time2 - time1, item }));
            });
        }

        private void WriteLogMessage(string source, string message)
        {
            try
            {
                if (!EventLog.SourceExists(source))
                {
                    EventLog.CreateEventSource(source, this._name);
                }
                new EventLog(this._name) { Source = source }.WriteEntry(message, EventLogEntryType.Information);
            }
            catch (InvalidEnumArgumentException ex)
            {
                WriteErrorSync(ex);
            }
            catch (SecurityException ex)
            {
                WriteErrorSync(ex);
            }
            catch (ArgumentException ex)
            {
                WriteErrorSync(ex);
            }
            catch (InvalidOperationException ex)
            {
                WriteErrorSync(ex);
            }
            catch (Win32Exception ex)
            {
                WriteErrorSync(ex);
            }
        }

        public static void WriteOutSync(Exception ex)
        {
            WriteOutSync(string.Format(CultureInfo.InvariantCulture, "{0}#{0}{1}{0}#{0}{2}{0}#{0}", new object[] { Environment.NewLine, ex.Message, ex.StackTrace }));
        }

        public static void WriteOutSync(string message)
        {
            TimeSpan time2 = new TimeSpan(DateTime.Now.Ticks + 1L);
            ThreadHelper.ActionDelegateWorkItem<string>.QuerySync(message, delegate (string item) {
                Console.Out.WriteLine(string.Format(CultureInfo.InvariantCulture, "[{0, 6}]:[{1}]:[{2}]", new object[] { ++outputMessageIndex, time2 - time1, item }));
            });
        }

        public static void WriteSync(string message)
        {
            TimeSpan time2 = new TimeSpan(DateTime.Now.Ticks + 1L);
            ThreadHelper.ActionDelegateWorkItem<string>.QuerySync(message, delegate(string item)
            {
                Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "[{0, 6}]:[{1}]:[{2}]", new object[] { ++messageIndex, time2 - time1, item }));
            });
        }

        public string Name
        {
            get
            {
                return this._name;
            }
        }
    }
}

